package middleware

import (
	"errors"
	"fmt"
	"git.mycaigou.com/gfyx/common/authorize"
	reportDto "git.mycaigou.com/gfyx/micro-gfyx-mp-api/http_server/dto/report"
	"git.mycaigou.com/gfyx/micro-gfyx-mp-api/service/base/user/user_service"
	reportService "git.mycaigou.com/gfyx/micro-gfyx-mp-api/service/clue/report"
	"git.myscrm.cn/golang/common/ykerrcode"
	"net/http"
	"strings"
	"time"

	cache2 "git.mycaigou.com/gfyx/common/cache"
	"git.mycaigou.com/gfyx/common/rpc_client"
	"git.mycaigou.com/gfyx/micro-gfyx-mp-api/infrastructure/common/errcode"
	"git.mycaigou.com/gfyx/micro-gfyx-mp-api/infrastructure/di"
	"git.mycaigou.com/gfyx/micro-gfyx-mp-api/infrastructure/utils/jwt"
	"git.myscrm.cn/golang/stark/v4"
	"github.com/gin-gonic/gin"
)

var (
	authorization  = "authorization"
	expectedScheme = "bearer"

	error_lack_authorization = "请求头缺少authorization字段"
)

func response(ctx *gin.Context, errorCode int, msg string) {
	result := gin.H{
		"success":    false,
		"message":    msg,
		"error_code": errorCode,
		"data":       nil,
	}

	ctx.JSON(http.StatusOK, result)
	ctx.Abort()
}

type HttpHeader struct {
	AuthToken string `json:"auth-token"`
}

// Authorize
// middleware
func Authorize() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		// 临时记录gin的请求
		authorizationStr := ctx.Request.Header.Get(authorization)
		stark.Logger.Info(ctx, "URI:", ctx.Request.RequestURI, ".BODY:", ctx.Request.Body, ".authorization:", authorizationStr)

		splits := strings.SplitN(authorizationStr, " ", 2)
		if len(splits) < 2 {
			response(ctx, errcode.COMMON_USER_IDENTITY_ERROR, error_lack_authorization)
			return
		}

		if !strings.EqualFold(splits[0], expectedScheme) {
			response(ctx, errcode.COMMON_USER_IDENTITY_ERROR, error_lack_authorization)
			return
		}

		jwtToken := splits[1]

		// jwt token
		claims, err := jwt.DefaultJWT.ParseToken(jwtToken)
		if err != nil {
			stark.Logger.Infof(ctx, "Authorize-parsejwt-error: %+v, claims: %+v", err, claims)
			response(ctx, errcode.COMMON_TOKEN_ERROR, "登录过期，刷新重登录")
			return
		}

		globalUserUid := claims.GlobalUserId
		tenantCode := claims.TenantCode
		userToken := claims.Tk
		tenantUserId := claims.TenantUserId
		tenantUserCode := claims.TenantUserCode
		tenantUserName := claims.TenantUserName
		mobilePhone := claims.MobilePhone
		tenantName := claims.TenantName
		userId := claims.UserId
		if tenantUserCode == "" {
			tenantUserCode = mobilePhone
		}
		if userToken == "" {
			stark.Logger.Infof(ctx, "Authorize-parsejwt-error1: %+v, claims: %+v", err, claims)
			response(ctx, errcode.COMMON_TOKEN_ERROR, "登录过期，刷新重登录")
			return
		}

		cache, _ := cache2.Redis()
		key := fmt.Sprintf("gfyx-%s", jwtToken)
		val, err := cache.Get(ctx, key)
		if err != nil {
			stark.Logger.Errorf(ctx, "Authorize-cache: %s, value: %v, err: %+v", key, val, err)
		}

		if val != userToken {
			c := di.InjectInstance()
			err = c.Invoke(func(userService user_service.UserSerivceIface) error {
				userInfo, _, err := userService.VerifyToken(ctx, userToken)
				if err != nil {
					return err
				}

				if userInfo == nil {
					return errors.New("用户不存在")
				}

				// 缓存1分钟
				_, err = cache.Set(ctx, key, userToken, true, 1*60)
				if err != nil {
					stark.Logger.Infof(
						ctx, "InjectInstance-cache-error: %+v, key: %s, userInfo: %+v", err, key, userInfo)
				}

				return nil
			})

			if err != nil {
				grpcErr := rpc_client.GetGrpcError(err)
				errCode := errcode.COMMON_TOKEN_ERROR
				errMessage := "登录过期，刷新重登录"
				if grpcErr != nil && grpcErr.GetCode() == 20020002 {
					errCode = errcode.COMMON_USER_FORBIDDEN
					errMessage = grpcErr.GetMessage()
				}

				stark.Logger.Infof(ctx, "Authorize-token: %s, err: %+v", jwtToken, err)
				response(ctx, errCode, errMessage)
				return
			}
		}

		// 如果租户 code 不为空，并且 header url_uid 参数里不能为空，从用户服务获取
		if tenantCode == "" && ctx.Request.Header.Get("url_uid") != "" {
			c := di.InjectInstance()
			err = c.Invoke(func(reportService reportService.ReportServiceIface) error {
				dto := &reportDto.ReportInfoRequestDto{
					Code: ctx.Request.Header.Get("url_uid"),
				}
				reportInfo, err := reportService.ReportInfo(ctx, dto)
				if err != nil {
					return err
				}
				if reportInfo != nil {
					tenantCode = reportInfo.TenantCode
				}
				return nil
			})
		}

		stark.Logger.Infof(ctx, "Authorize-success: jwttoken: %s, globalUserUid: %s", jwtToken, globalUserUid)

		// globalUserUid
		ctx.Set(authorize.GLOBAL_USER_ID, globalUserUid)
		ctx.Set(authorize.TENANT_CODE_KEY, tenantCode)
		ctx.Set(authorize.TENANT_USER_ID, tenantUserId)
		ctx.Set(authorize.TENANT_USER_CODE, tenantUserCode)
		ctx.Set(authorize.TENANT_USER_NAME, tenantUserName)
		ctx.Set(authorize.MOBILE_PHONE, mobilePhone)
		ctx.Set(authorize.TENANT_NAME_KEY, tenantName)
		ctx.Set(authorize.LOGIN_USER_ID, userId)
		//if err != nil {
		//	stark.Logger.Errorf(ctx, "设置用户元数据失败 %+v", err)
		//	response(ctx, errcode.COMMON_TOKEN_ERROR, "设置用户元数据失败"+err.Error())
		//	return
		//}
		ctx.Next()
	}
}

// SyncUserAuthorize
// 用户同步验证
func SyncUserAuthorize() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		authToken := ctx.Request.Header.Get("auth_token")
		syncuserAuthToken := stark.Configer.(stark.ApolloConfigerIface).GetStringValue("syncuser_auth_token", "")
		if authToken != syncuserAuthToken {
			stark.Logger.Warnf(ctx, "token验证失败，auth_token: %s", authToken)
			response(ctx, errcode.COMMON_TOKEN_ERROR, "token验证失败")
			return
		}
		ctx.Next()
	}
}

// 开放接口鉴权
func OpenAuthorize() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		authorizationStr := ctx.Request.Header.Get("authorization")
		splits := strings.SplitN(authorizationStr, " ", 2)
		if len(splits) < 2 {
			response(ctx, ykerrcode.ACCESS_DENIED, error_lack_authorization)
			return
		}

		if !strings.EqualFold(splits[0], expectedScheme) {
			response(ctx, ykerrcode.ACCESS_DENIED, error_lack_authorization)
			return
		}
		appid := ctx.Request.Header.Get("appid")
		if appid == "" {
			response(ctx, ykerrcode.ACCESS_DENIED, "appid不能为空")
			return
		}
		accessToken := splits[1]

		// todo 去掉 模拟生成access_token
		//testsignKey := "test666"
		////todo 从阿波罗取 vars gfyxSignKey = stark.Configer.GetStringValue("gfyx_model_main_key", "")
		//accessToken, err := jwt.DefaultJWT.GenerateServiceAuthToken("mysoft", "a=b;c=d", testsignKey)
		//if err != nil {
		//	response(ctx, errcode.COMMON_PARAMS_ERROR, "accessToken生成失败")
		//	return
		//}

		signKey := stark.Configer.(stark.ApolloConfigerIface).GetStringValue("Auth."+appid+".AppKey", "")
		if signKey == "" {
			stark.Logger.Errorf(ctx, "当前应用没有权限访问 authorization:%s appid:%v", authorizationStr, appid)
			response(ctx, ykerrcode.ACCESS_DENIED, "签名时间超过两个小时")
			return
		}

		claims, err := jwt.DefaultJWT.ParseServiceAuthToken(accessToken, signKey)
		if err != nil {
			stark.Logger.Errorf(ctx, "authorization验证失败 authorization:%s err:%v", authorizationStr, err)
			response(ctx, ykerrcode.ACCESS_DENIED, "authorization验证失败 "+err.Error())
			return
		}
		if claims.A == "" {
			stark.Logger.Errorf(ctx, "应用标志不能为空 authorization:%s claims:%v", authorizationStr, claims)
			response(ctx, ykerrcode.ACCESS_DENIED, "应用标志不能为空")
			return
		}
		if claims.IssuedAt == 0 {
			stark.Logger.Errorf(ctx, "签发时间不能为空 authorization:%s claims:%v", authorizationStr, claims)
			response(ctx, ykerrcode.ACCESS_DENIED, "签发时间不能为空")
			return
		}
		issuedAt := time.Unix(claims.IssuedAt, 0)
		currentTime := time.Now()
		timeDiff := currentTime.Sub(issuedAt)
		hoursDiff := timeDiff.Hours()

		if hoursDiff > 2 {
			// 签名时间超过两个小时的处理...
			stark.Logger.Errorf(ctx, "签名时间超过两个小时 authorization:%s claims:%v", authorizationStr, claims)
			response(ctx, ykerrcode.ACCESS_DENIED, "签名时间超过两个小时")
			return
		}

		ctx.Next()
	}
}
